package com.piece.core.oauth.filter;

import com.piece.core.framework.constant.ApplicationConstants;
import com.piece.core.framework.constant.FrameWorkConstants;
import com.piece.core.framework.constant.ParamConstants;
import com.piece.core.framework.constant.ExceptionAuthConstants;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.verifycode.enums.ValidateCodeType;
import com.piece.core.verifycode.exception.ValidateCodeException;
import com.piece.core.verifycode.processor.ValidateCodeProcessorHolder;
import com.piece.core.verifycode.util.ValidateCodeUtil;
import com.piece.core.web.properties.SecurityProperties;
import com.piece.core.web.util.AuthenticationUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 验证码过滤器
 */
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    private final ApplicationProperties applicationProperties;
    // 存放所有需要校验验证码的url
    private Set<String> validateUrl = new HashSet<>();
    private AntPathMatcher pathMatcher = new AntPathMatcher();

    private ValidateCodeProcessorHolder validateCodeProcessorHolder;
    private AuthenticationFailureHandler authenticationFailureHandler;

    public ValidateCodeFilter(ApplicationProperties applicationProperties, SecurityProperties securityProperties) {
        this.applicationProperties = applicationProperties;
        addUrlToMap(securityProperties.getProcess().getLoginProcessUrl());
        addUrlToMap(securityProperties.getProcess().getRegisterProcessUrl());
    }

    /**
     * 初始化要拦截的url配置信息
     */
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
    }

    /**
     * 将系统中配置的需要校验验证码的URL放入set集合
     */
    protected void addUrlToMap(String urlString) {
        if (StringUtil.isNotBlank(urlString)) {
            String[] urls = StringUtil.splitByWholeSeparatorPreserveAllTokens(urlString, FrameWorkConstants.SPLIT_PARAM);
            for (String url : urls) {
                validateUrl.add(url);
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (pathMatcher(request)) {
            boolean needValidate = true;
            String clientId = AuthenticationUtil.getInstance(request).extractClientId();

            String ignore = applicationProperties.getValue(ApplicationConstants.CLIENT_IGNORE_VALIDATE);
            if (null != clientId && null != ignore) {
                if (StringUtil.getIndexOfString(clientId, Convert.toStrArray(ignore)) != -1) {
                    needValidate = false;
                }
            }

            if (needValidate && "POST".equalsIgnoreCase(request.getMethod())) {
                // 验证码校验
                ValidateCodeType type = ValidateCodeUtil.getValidateCodeType(request.getParameter(ParamConstants.GRANT_TYPE));
                try {
                    String codeSession = request.getParameter(ParamConstants.VALIDATE_CODE_SESSION);
                    String validCode = request.getParameter(ParamConstants.VALIDATE_CODE_CAPTCHA);
                    if (StringUtil.isEmpty(codeSession)) {
                        throw new ValidateCodeException(I18nUtil.message(ExceptionAuthConstants.VALIDATE_CODE_EXPIRED));
                    }
                    validateCodeProcessorHolder.findValidateCodeProcessor(type).validate(codeSession, validCode);
                } catch (Exception e) {
                    authenticationFailureHandler.onAuthenticationFailure(request, response, new ValidateCodeException(e.getMessage()));
                    return;
                }
            }
        }

        chain.doFilter(request, response);
    }

    private boolean pathMatcher(HttpServletRequest request) {
        for (String url : validateUrl) {
            if (pathMatcher.match(url, request.getServletPath())) {
                return true;
            }
        }

        return false;
    }

    public void setValidateCodeProcessorHolder(ValidateCodeProcessorHolder validateCodeProcessorHolder) {
        this.validateCodeProcessorHolder = validateCodeProcessorHolder;
    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }
}
